import type { Recordable } from '@/typings'

/**
 * 显示加载提示
 * @param options 配置选项
 * @param options.title 提示文字，默认为 '加载中...'
 * @param options.mask 是否显示透明蒙层，防止触摸穿透，默认为 true
 */
export function showLoad(options: { title?: string, mask?: boolean } = {}): void {
  const { title = '加载中...', mask = true } = options
  uni.showLoading({
    title,
    mask,
  })
}

/**
 * 隐藏加载提示
 */
export function hideLoad(): void {
  uni.hideLoading()
}

/**
 * 显示消息提示框
 * @param options 配置选项
 * @param options.title 提示的内容
 * @param options.icon 图标类型，默认为 'none'
 * @param options.duration 提示的延迟时间，默认为 2000 毫秒
 * @param options.mask 是否显示透明蒙层，防止触摸穿透，默认为 false
 */
export function showToast(options: {
  title: string
  icon?: 'success' | 'error' | 'loading' | 'none'
  duration?: number
  mask?: boolean
}): void {
  const { title, icon = 'none', duration = 2000, mask = false } = options
  uni.showToast({
    title,
    icon,
    duration,
    mask,
  })
}

/**
 * 显示成功提示
 * @param options 配置选项
 * @param options.title 提示内容
 * @param options.duration 提示延迟时间，默认为 2000 毫秒
 */
export function showSuccess(options: { title: string, duration?: number }): void {
  const { title, duration = 2000 } = options
  showToast({ title, icon: 'success', duration })
}

/**
 * 显示错误提示
 * @param options 配置选项
 * @param options.title 提示内容
 * @param options.duration 提示延迟时间，默认为 2000 毫秒
 */
export function showError(options: { title: string, duration?: number }): void {
  const { title, duration = 2000 } = options
  showToast({ title, icon: 'error', duration })
}

/**
 * 隐藏消息提示框
 */
export function hideToast(): void {
  uni.hideToast()
}

/**
 * 显示模态对话框
 * @param options 配置选项
 * @param options.title 提示的标题
 * @param options.content 提示的内容
 * @param options.showCancel 是否显示取消按钮，默认为 true
 * @param options.cancelText 取消按钮的文字，默认为 '取消'
 * @param options.confirmText 确定按钮的文字，默认为 '确定'
 * @returns Promise<boolean> 用户点击确定返回 true，点击取消返回 false
 */
export function showModal(options: {
  title: string
  content: string
  showCancel?: boolean
  cancelText?: string
  confirmText?: string
}): Promise<boolean> {
  const {
    title,
    content,
    showCancel = true,
    cancelText = '取消',
    confirmText = '确定',
  } = options
  return new Promise((resolve) => {
    uni.showModal({
      title,
      content,
      showCancel,
      cancelText,
      confirmText,
      success: (res) => {
        resolve(res.confirm)
      },
      fail: () => {
        resolve(false)
      },
    })
  })
}

/**
 * 显示操作菜单
 * @param options 配置选项
 * @param options.itemList 按钮的文字数组
 * @param options.itemColor 按钮的文字颜色，默认为 '#000000'
 * @returns Promise<number> 用户点击的按钮序号，从0开始，点击取消或蒙层返回 -1
 */
export function showActionSheet(options: {
  itemList: string[]
  itemColor?: string
}): Promise<number> {
  const { itemList, itemColor = '#000000' } = options
  return new Promise((resolve) => {
    uni.showActionSheet({
      itemList,
      itemColor,
      success: (res) => {
        resolve(res.tapIndex)
      },
      fail: () => {
        resolve(-1)
      },
    })
  })
}

/**
 * 页面跳转 - 保留当前页面，跳转到应用内的某个页面
 * @param options 配置选项
 * @param options.url 需要跳转的应用内非 tabBar 的页面的路径
 * @param options.params 页面参数对象
 */
export function navigateTo(options: { url: string, params?: Recordable }): void {
  const { url, params } = options
  const fullUrl = params ? `${url}?${objectToUrlParams(params)}` : url
  uni.navigateTo({ url: fullUrl })
}

/**
 * 页面跳转 - 关闭当前页面，跳转到应用内的某个页面
 * @param options 配置选项
 * @param options.url 需要跳转的应用内非 tabBar 的页面的路径
 * @param options.params 页面参数对象
 */
export function redirectTo(options: { url: string, params?: Recordable }): void {
  const { url, params } = options
  const fullUrl = params ? `${url}?${objectToUrlParams(params)}` : url
  uni.redirectTo({ url: fullUrl })
}

/**
 * 页面跳转 - 跳转到 tabBar 页面，并关闭其他所有非 tabBar 页面
 * @param options 配置选项
 * @param options.url 需要跳转的 tabBar 页面的路径
 * @param options.params 页面参数对象（注意：tabBar 页面不支持参数传递）
 */
export function switchTab(options: { url: string, params?: Recordable }): void {
  const { url } = options
  // tabBar 页面不支持参数，这里仅为保持接口一致性
  uni.switchTab({ url })
}

/**
 * 页面跳转 - 关闭所有页面，打开到应用内的某个页面
 * @param options 配置选项
 * @param options.url 需要跳转的应用内页面路径
 * @param options.params 页面参数对象
 */
export function reLaunch(options: { url: string, params?: Recordable }): void {
  const { url, params } = options
  const fullUrl = params ? `${url}?${objectToUrlParams(params)}` : url
  uni.reLaunch({ url: fullUrl })
}

/**
 * 页面返回
 * @param options 配置选项
 * @param options.delta 返回的页面数，如果 delta 大于现有页面数，则返回到首页，默认为 1
 */
export function navigateBack(options: { delta?: number } = {}): void {
  const { delta = 1 } = options
  uni.navigateBack({ delta })
}

/**
 * 获取当前页面栈
 * @returns 当前页面栈实例
 */
export function getCurrentPages(): any[] {
  return getCurrentPages()
}

/**
 * 获取当前页面路径
 * @returns 当前页面路径
 */
export function getCurrentRoute(): string {
  const pages = getCurrentPages()
  const currentPage = pages[pages.length - 1]
  return currentPage ? `/${currentPage.route}` : ''
}

/**
 * 设置导航栏标题
 * @param options 配置选项
 * @param options.title 页面标题
 */
export function setNavigationBarTitle(options: { title: string }): void {
  const { title } = options
  uni.setNavigationBarTitle({ title })
}

/**
 * 设置导航栏颜色
 * @param options 配置选项
 * @param options.frontColor 前景颜色值，包括按钮、标题、状态栏的颜色，仅支持 #ffffff 和 #000000
 * @param options.backgroundColor 背景颜色值，有效值为十六进制颜色
 * @param options.animation 动画效果
 */
export function setNavigationBarColor(options: {
  frontColor: '#ffffff' | '#000000'
  backgroundColor: string
  animation?: {
    duration: number
    timingFunc: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'
  }
}): void {
  const { frontColor, backgroundColor, animation } = options
  uni.setNavigationBarColor({
    frontColor,
    backgroundColor,
    animation,
  })
}

/**
 * 显示导航栏加载动画
 */
export function showNavigationBarLoading(): void {
  uni.showNavigationBarLoading()
}

/**
 * 隐藏导航栏加载动画
 */
export function hideNavigationBarLoading(): void {
  uni.hideNavigationBarLoading()
}

/**
 * 复制文本到剪贴板
 * @param options 配置选项
 * @param options.data 需要复制的内容
 * @returns Promise<boolean> 复制是否成功
 */
export function setClipboardData(options: { data: string }): Promise<boolean> {
  const { data } = options
  return new Promise((resolve) => {
    uni.setClipboardData({
      data,
      success: () => {
        resolve(true)
      },
      fail: () => {
        resolve(false)
      },
    })
  })
}

/**
 * 获取剪贴板内容
 * @returns Promise<string> 剪贴板内容
 */
export function getClipboardData(): Promise<string> {
  return new Promise((resolve) => {
    uni.getClipboardData({
      success: (res) => {
        resolve(res.data)
      },
      fail: () => {
        resolve('')
      },
    })
  })
}

/**
 * 震动反馈
 * @param options 配置选项
 * @param options.type 震动强度类型，默认为 'medium'
 */
export function vibrateShort(options: { type?: 'heavy' | 'medium' | 'light' } = {}): void {
  const { type = 'medium' } = options
  // #ifdef MP-WEIXIN
  uni.vibrateShort({ type })
  // #endif
  // #ifndef MP-WEIXIN
  uni.vibrateShort()
  // #endif
}

/**
 * 长震动
 */
export function vibrateLong(): void {
  uni.vibrateLong()
}

/**
 * 保存图片到相册
 * @param options 配置选项
 * @param options.filePath 图片文件路径，可以是临时文件路径或永久文件路径
 * @returns Promise<boolean> 保存是否成功
 */
export function saveImageToPhotosAlbum(options: { filePath: string }): Promise<boolean> {
  const { filePath } = options
  return new Promise((resolve) => {
    uni.saveImageToPhotosAlbum({
      filePath,
      success: () => {
        resolve(true)
      },
      fail: () => {
        resolve(false)
      },
    })
  })
}

/**
 * 预览图片
 * @param options 配置选项
 * @param options.urls 需要预览的图片链接列表
 * @param options.current 当前显示图片的链接，默认为 urls 的第一张
 */
export function previewImage(options: { urls: string[], current?: string }): void {
  const { urls, current } = options
  uni.previewImage({
    urls,
    current: current || urls[0],
  })
}

/**
 * 选择图片
 * @param options 配置选项
 * @param options.count 最多可以选择的图片张数，默认为 1
 * @param options.sizeType 所选的图片的尺寸，默认为 ['original', 'compressed']
 * @param options.sourceType 选择图片的来源，默认为 ['album', 'camera']
 * @returns Promise<string[]> 图片的本地文件路径列表
 */
export function chooseImage(options: {
  count?: number
  sizeType?: ('original' | 'compressed')[]
  sourceType?: ('album' | 'camera')[]
} = {}): Promise<string[]> {
  const {
    count = 1,
    sizeType = ['original', 'compressed'],
    sourceType = ['album', 'camera'],
  } = options
  return new Promise((resolve) => {
    uni.chooseImage({
      count,
      sizeType,
      sourceType,
      success: (res) => {
        const paths = Array.isArray(res.tempFilePaths) ? res.tempFilePaths : [res.tempFilePaths].filter(Boolean)
        resolve(paths)
      },
      fail: () => {
        resolve([])
      },
    })
  })
}

/**
 * 对象转 URL 参数字符串
 * @param obj 参数对象
 * @returns URL 参数字符串
 */
function objectToUrlParams(obj: Recordable): string {
  const params = Object.keys(obj)
    .filter(key => obj[key] !== undefined && obj[key] !== null && obj[key] !== '')
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(String(obj[key]))}`)
    .join('&')
  return params
}
